<?php

namespace StudyBuddy;

use \StudyBuddy\OFacebook;

/**
 * Class to login or create new account (and then login)
 * the Facebook user
 *
 * @todo Do something if user with the same email already exists
 *
 * @todo post event on user creation, on user update
 * if just adding record to USERS_FACEBOOK for existing user
 * or if updating user data in any way.
 *
 * @todo
 * run post-registration post to wall if Admin set this in config
 *
 * @todo send out registration email after createNewUser() is run
 * send out special email that explains that user can login
 * with Facebook button as before OR directly with
 * new username/password
 * For this the sendRegistrationEmail() should be in some external
 * class that would accept email, body
 * Probably in Mailer class as static method
 *
 * @todo this class should extend Facebook so that we
 * may reuse removeFacebookConnect()
 *
 */
class ExternalAuthFb extends Facebook {

    /**
     * Parsed facebook cookie
     *
     * @var array
     */
    protected $aCookieParams = array();

    /**
     * Array of data returned from Facebook server
     * @var array
     */
    protected $aFbUserData = array();

    /**
     * Indicates if post announcment of new
     * registation to user's FB Wall
     *
     * @var bool
     */
    protected $bToWall = false;

    /**
     * Auto generated password for the new user
     *
     * @todo must also check that curl has support for ssl
     * because oAuth2 uses ssl
     *
     * @param object $oRegistry
     * @param array $aFacebook array from !config.ini FACEBOOK section
     * @param array $aCookieParams
     */
    protected $tempPassword;

    protected function __construct(Registry $oRegistry, array $aFacebookConf, array $aCookieParams) {
        if (!extension_loaded('curl')) {
            throw new \StudyBuddy\Exception('Cannot use this class because php extension "curl" is not loaded');
        }

        parent::__construct($oRegistry);

        $oRegistry->Mongo->USERS->ensureIndex(array('fb_id' => 1));

        d('$this->oUser: ' . get_class($this->oUser) . ' ' . print_r($this->oUser->getArrayCopy(), 1));
        $this->sAccessToken = $aCookieParams['access_token'];
        $this->sAppId = $aFacebookConf['APP_ID'];
        $this->bToWall = (!empty($aFacebookConf['POST_TO_WALL'])) ? true : false;
        $this->aCookieParams = $aCookieParams;

        // Facebook-php-v3.1.1
//        $this->oFacebook = new OFacebook(array(
//            'appId'  => $aFacebookConf['APP_ID'],
//            'secret' => $aFacebookConf['APP_SECRET']
//        ));
    }

    /**
     * Get user data from Facebook, do whatever is necessary
     * and return either null or object of type UserFacebook
     * which extends User Object
     *
     * @param Registry $oRegistry
     *
     * @param bool $bIsConnect if set to true then this method
     * is called for the purpose of "Connecting" existing user to
     * the Facebook Account. This this case we check that Facebook
     * user with the same Facebook account does not already exist
     *
     * @return mixed null of failure or object UserFacebook
     *
     * @throws FacebookAuthException in case user does not have
     * fbsr_ cookie or in case this site config does not have
     * necessary settings in [FACEBOOK] section
     * or in case something else goes wrong
     */
    public static function getUserObject(Registry $oRegistry) {

        $aFacebookConf = $oRegistry->Ini->getSection('FACEBOOK');
        // facebook-php-3.1.1
        $oFacebook = new OFacebook(array(
                    'appId' => $aFacebookConf['APP_ID'],
                    'secret' => $aFacebookConf['APP_SECRET']
                ));
        $user = $oFacebook->getUser();

        $aCookieParams = self::prepareFBCookies($aFacebookConf);

        /**
         * At this point we can try to get user
         * just by facebook uid which should be in fb cookie
         * If we can get uid by fbid and then userobject by uid
         * then we can defer calling FB api to later time,
         * but we can't just call fastcgi_finish_request now
         * because it would send out something to the browser now
         * and we still at early stage of page generation.
         *
         */
//        if (!empty($aCookieParams['uid'])) {
//            d('going to try to get user object by fbu uid cookie: ' . $aCookieParams['uid']);
//            $oRegistry->Mongo->USERS->ensureIndex(array('fb_id' => 1));
//            $aUser = $oRegistry->Mongo->USERS->findOne(array('fb_id' => (string) $aCookieParams['uid']));
//            if (!empty($aUser)) {
        /**
         * Should check if user still has FB fb_token?
         * If FB access was revoked why should be still
         * return this user object? It's OK, its still our valid
         * user
         */
//                d('Found user by fb cookie in USERS');
//                $oUser = UserFacebook::factory($oRegistry, $aUser);
//            }


        /**
         * If we able to find our user by facebook user id
         * we will add the job of contacting facebook api
         * for possible update of data to be executed
         * later
         *
         * But if we did not get facebook user by facebook uid
         * that means it's a new user and we must finish
         * this method now in order to create a new user
         *
         */
//            if (isset($oUser)) {
//                d('got FB user by fb uid from fbu cookie');
//
//                $oRegistry->Viewer = $oUser;
//                $oAuthFB = new self($oRegistry, $aFacebookConf, $aCookieParams);
//                $callable = function() use ($oAuthFB) {
//                            try {
        //d('before facebook auth post precessing  $oAuthFB: '.$oAuthFB);
//                                $oAuthFB->getFbData()->getFacebookUserObject();
        //d('after facebook auth post precessing');
//                            } catch (Exception $e) {
        //e('Unable to run post processing of FB data: '.$e->getFile().' '.$e->getLine().' '.$e->getMessage());
//                            }
//                        };
//
//                d('cp before runLater');
//                runLater($callable);
//                d('after runLater');
//
//                return $oUser;
//            }
//        }
        // facebook-php-3.1.1
        if ($user) {
            d('going to try to get user object by fbu uid cookie: ' . $user);
            $oRegistry->Mongo->USERS->ensureIndex(array('fb_id' => 1));
            $aUser = $oRegistry->Mongo->USERS->findOne(array('fb_id' => (string) $user));
            if (!empty($aUser)) {
                /**
                 * Should check if user still has FB fb_token?
                 * If FB access was revoked why should be still
                 * return this user object? It's OK, its still our valid
                 * user
                 */
                d('Found user by fb cookie in USERS');
                $oUser = UserFacebook::factory($oRegistry, $aUser);
            }


            /**
             * If we able to find our user by facebook user id
             * we will add the job of contacting facebook api
             * for possible update of data to be executed
             * later
             *
             * But if we did not get facebook user by facebook uid
             * that means it's a new user and we must finish
             * this method now in order to create a new user
             *
             */
            if (isset($oUser)) {
                d('got FB user by fb uid from fbu cookie');

                $oRegistry->Viewer = $oUser;
                $oAuthFB = new self($oRegistry, $aFacebookConf, $aCookieParams);
                $callable = function() use ($oAuthFB) {
                            try {
                                //d('before facebook auth post precessing  $oAuthFB: '.$oAuthFB);
                                $oAuthFB->getFbData()->getFacebookUserObject();
                                //d('after facebook auth post precessing');
                            } catch (Exception $e) {
                                //e('Unable to run post processing of FB data: '.$e->getFile().' '.$e->getLine().' '.$e->getMessage());
                            }
                        };

                d('cp before runLater');
                runLater($callable);
                d('after runLater');

                return $oUser;
            }
        }

        $oAuthFB = new self($oRegistry, $aFacebookConf, $aCookieParams);

        return $oAuthFB->getFbData()->getFacebookUserObject();
    }

    /**
     * Factory method
     *
     * @param Registry $oRegistry
     * @return object of this class
     * @throws FacebookAuthException in case Cookie that is supposed
     * to be set by Facebook JS was not or is not valid
     */
    public static function factory(Registry $oRegistry) {

        $aFacebookConf = $oRegistry->Ini->getSection('FACEBOOK');
        // facebook-php-3.1.1
        $oFacebook = new OFacebook(array(
                    'appId' => $aFacebookConf['APP_ID'],
                    'secret' => $aFacebookConf['APP_SECRET']
                ));
        $aCookieParams = self::prepareFBCookies($aFacebookConf, $oFacebook);
        return new self($oRegistry, $aFacebookConf, $aCookieParams);
    }

    /**
     * Parse array $aFacebookConf
     * then get, parse and validate cookie set by Facebook JS API
     * If everhing looks good return array parsed cookie
     *
     * @param array $aFacebookConf
     * @throws FacebookAuthException if the required params
     * in !config.ini in 'FACEBOOK' section is not set
     * OR if Facebook cookie does not look valid
     *
     * @return array of parsed cookie
     */
    public static function prepareFBCookies(array $aFacebookConf) {
        if (empty($aFacebookConf) || (is_array($aFacebookConf)
                && (empty($aFacebookConf['APP_ID']) || empty($aFacebookConf['APP_SECRET']) ) )) {
            throw new FacebookAuthUserException('Administrator of this site has not enabled Facebook connect feature');
        }

        $sAppId = $aFacebookConf['APP_ID'];
        $sSecret = $aFacebookConf['APP_SECRET'];

        $cookieName = 'fbsr_' . $sAppId;
        if (!isset($_COOKIE) || empty($_COOKIE[$cookieName])) {
            throw new FacebookAuthException('No fbsr_ cookie present');
        }

        $cookie = $_COOKIE[$cookieName];

        $aCookieParams = array();
//        parse_str(trim($cookie, '\\"'), $aCookieParams);
        // facebook-php-3.1.1
        $oFacebook = new OFacebook(array(
                    'appId' => $aFacebookConf['APP_ID'],
                    'secret' => $aFacebookConf['APP_SECRET']
                ));
        $user = $oFacebook->getUser();
        if (!$user) {
            throw new FacebookAuthException('No user present');
        } else {
            $aCookieParams = array(
                'uid' => $user,
                'access_token' => $oFacebook->getAccessToken()
            );
        }
        d('$aCookieParams: ' . print_r($aCookieParams, 1));

        if (empty($aCookieParams)
//                || empty($aCookieParams['sig'])
                || empty($aCookieParams['access_token'])
        ) {

            throw new FacebookAuthException('Unable to parse fbsr_ cookie: ' . $cookie);
        }

        /**
         * Security check of fbsr cookie
         */
//        if ($aCookieParams['sig'] !== self::generateSignature($aCookieParams, $sSecret)) {
//
//            throw new FacebookAuthException('Facebook signature violation. Potential security threat! ' . print_r($aCookieParams, 1));
//        }

        return $aCookieParams;
    }

    /**
     * Generate a signature for the given params and secret.
     *
     * @param Array $params the parameters to sign
     * @param String $secret the secret to sign with
     * @return String the generated signature
     */
    protected static function generateSignature(array $params, $secret) {

        ksort($params);

        $s = '';
        foreach ($params as $key => $value) {
            if ($key != 'sig') {
                $s .= $key . '=' . $value;
            }
        }

        $s .= $secret;

        return hash('md5', $s);
    }

    /**
     * Return object of type FacebookUser
     * this is either the existing user or newly created
     * user
     *
     * @return object of type UserFacebook which extends User Object
     *
     * @throws FacebookAuthException in case something goes wrong
     */
    public function getFacebookUserObject() {
        d('cp');

        /**
         * First get userid by fb_id, via cache
         * even though this is usually less than 1 millisecond,
         * still avoiding mysql call is good.
         *
         */
        $aUser = $this->getUserArray($this->aFbUserData['id']);
        if (!empty($aUser)) {
            $this->oUser = UserFacebook::factory($this->oRegistry, $aUser);
            d('existing user $this->oUser: ' . print_r($this->oUser->getArrayCopy(), 1));
            $this->updateUser()->updateFbUserRecord();
            d('cp');

            return $this->oUser;
        }


        /**
         * See if we already have the user with the email
         * address provided by facebook.
         * In such case we just create the record in USERS_FACEBOOK
         * And possibly run updateUser()
         * And then.... append array of access_token, expires
         * to the object
         *
         * @todo potential problem:
         * someone registers bogus account with someone else's email
         * address.
         *
         * Then the real owner of that email registers via Facebook
         * We then associate some bogus account with this one
         *
         * The bogus account cannot be used by hacker because hacker does
         * not know the password so this is not a big problem.
         *
         *
         */
        if (!empty($this->aFbUserData['email'])) {
            $aByEmail = $this->oRegistry->Mongo->EMAILS->findOne(array('email' => strtolower($this->aFbUserData['email'])));
            d('$aByEmail: ' . print_r($aByEmail, 1));
            if (!empty($aByEmail) && !empty($aByEmail['i_uid'])) {
                $uidByEmail = (int) $aByEmail['i_uid'];
                d('$uidByEmail: ' . $uidByEmail);
            }
        }


        /**
         * This means this facebook user is not
         * registered on our site.
         * Not found either by facebook id or by
         * email address. We are confident that this is
         * NOT an existing Facebook user.
         */
        if (empty($uidByEmail)) {
            d('cp empty uid');
            $this->createNewUser();

            return $this->oUser;
        }

        $aUser = $this->oRegistry->Mongo->USERS->findOne(array('_id' => $uidByEmail));
        d('aUser var type: ' . gettype($aUser) . ' ' . print_r($aUser, 1));

        /**
         * Found existing user
         * If this is a Connect action then check if this is
         * not the same user as Viewer and throw exception
         * if this is the same uid as Viewer then just update
         * Viewer record, it's OK and actually in case Viwer had FB
         * access revoked before this will update their access back
         * to "active" FB user by adding valid FB token to User object
         */
        if (!empty($aUser)) {
            $this->oUser = UserFacebook::factory($this->oRegistry, $aUser);
            d('existing user $this->oUser: ' . print_r($this->oUser->getArrayCopy(), 1));
            $this->updateUser();

            /**
             * It's possible that this is not the new user
             * but also a new FACEBOOK user.
             * This is when we determined that user with this email
             * already exists in our database but
             * this user has never logged in as Facebook user
             * in this case we still have to create a new
             * record in USERS_FACEBOOK
             */
            $this->updateFbUserRecord();
        } else {
            /**
             * This is the case where we found $uid either is USERS_FACEBOOK
             * or in EMAILS but then were unable to find
             * this user in USERS collection.
             * This is a very unlikely situation, not sure how
             * this could be possible....
             */
//			e('Very unlikely situation occured found uid: '.$uid.' but no user in USERS. ');
            d('cp need to create new user');
            $this->createNewUser();
        }

        return $this->oUser;
    }

    /**
     * Get JSON data from the server for this user
     * If timeout, then what? Then we will throw our own
     * Exception and user will see a message
     * that timeout has occured
     *
     * @return object $this
     */
    public function getFbData() {
        $aFacebookConf = $this->oRegistry->Ini->getSection('FACEBOOK');
        // facebook-php-3.1.1
        $oFacebook = new OFacebook(array(
                    'appId' => $aFacebookConf['APP_ID'],
                    'secret' => $aFacebookConf['APP_SECRET']
                ));
        d('$this->oUser: ' . get_class($this->oUser) . ' ' . print_r($this->oUser->getArrayCopy(), 1));
        d('This is: ' . gettype($this) . (is_object($this)) ? get_class($this) : 'not object');
//        $url = $this->graphUrl . $this->sAccessToken;
//        d('url: ' . $url);
//        $oHTTP = new Curl();

        try {
            d('cp');
//            $this->oResponse = $oHTTP->getDocument($url)->checkResponse();
//            $json = $this->oResponse->getResponseBody();
            
            $user_profile = $oFacebook->api('/me');
            /**
             * retCode should be 200
             *
             */
//            $retCode = $oHTTP->getHttpResponseCode();
//            d('json ' . $json . ' http code: ' . $retCode);

            /**
             * samle json data, can be used for mock object
             * in testing
             * {
              "id": "100000742465943",
              "name": "study buddy",
              "first_name": "study",
              "last_name": "buddy",
              "link": "http://www.facebook.com/profile.php?id=100000742465943",
              "gender": "male",
              "email": "d.snytkine\u0040gmail.com",
              "timezone": -4,
              "locale": "en_US",
              "verified": true,
              "updated_time": "2011-03-22T18:02:41+0000"
              }
             *
             */
//            $this->aFbUserData = json_decode($json, true);
            $this->aFbUserData = $user_profile;
            d('$this->aFbUserData: ' . print_r($this->aFbUserData, 1));

            if (empty($this->aFbUserData)
                    || !is_array($this->aFbUserData)
                    || !array_key_exists('id', $this->aFbUserData)
                    || empty($this->aFbUserData['name'])) {

                throw new FacebookAuthException('Invalid data returned by FriendConnect server: ' . print_r($this->aFbUserData, 1));
                ;
            }
        } catch (HttpTimeoutException $e) {
            d('Request to GFC server timedout');

            throw new FacebookAuthUserException('Request to Facebook server timed out. Please try again later');
        } catch (Http401Exception $e) {
            d('Unauthorized to get data from Facebook, most likely user unjoined the site');
            $this->revokeFacebookConnect();
            Cookie::delete('fbsr_' . $this->sAppId);

            throw new FacebookAuthUserException('Unauthorized with Facebook server');
        } catch (HttpResponseCodeException $e) {
            e('StudyBuddyError Facebook response exception: ' . $e->getHttpCode() . ' ' . $e->getMessage());
            /**
             * The non-200 response code means there is some kind
             * of error, maybe authorization failed or something like that,
             * or maybe Facebook Connect server was acting up.
             * In this case it is better to delete fcauth cookies
             * so that we dont go through these steps again.
             * User will just have to re-do the login Facebook step
             */
            Cookie::delete('fbsr_' . $this->sAppId);
            $this->revokeFacebookConnect();

            throw new FacebookAuthUserException('Error during authentication with Facebook server');
        }

        return $this;
    }

    /**
     * Update user data in USERS collection
     * by using $this->oUser object's save() method
     *
     * @return object $this
     */
    protected function updateUser($updateAvatar = true) {
        d('cp');

        $this->oUser['fb_id'] = (string) $this->aFbUserData['id'];
        $this->oUser['fb_token'] = $this->aCookieParams['access_token'];
        $this->oUser['fn'] = $this->aFbUserData['first_name'];
        $this->oUser['ln'] = $this->aFbUserData['last_name'];
        $extAvatar = $this->oUser['avatar_external'];

        /**
         * Reason why not checking $updateAvatar anymore
         * is because if logged in with Twitter,
         * then added FB using Connect - the avatar is not
         * updated (good)
         * but then same user loggs in with FB and
         * at that time $updateAvatar = false not passed
         * and avatar is then just changed to FB!
         * Unexpected turn of events!
         */
        if (empty($extAvatar)) {
            $this->oUser['avatar_external'] = 'http://graph.facebook.com/' . $this->aFbUserData['id'] . '/picture';
        }

        if (!empty($this->aFbUserData['link'])) {
            $this->oUser['fb_url'] = $this->aFbUserData['link'];
        }

        try {
            $this->oUser->save();
            d('cp');

            $this->oRegistry->Dispatcher->post($this->oUser, 'onUserUpdate');
            d('cp');
        } catch (\Exception $e) {
            e('Error while saving user: ' . $e->getMessage() . ' file: ' . $e->getFile() . ' on line ' . $e->getLine());
        }


        return $this;
    }

    /**
     * @todo
     * What if email address provided from Facebook
     * already belongs to some other user?
     *
     * This would mean that existing user is just
     * trying to signup with Facebook.
     *
     * In this case we should allow it but ONLY create
     * a record in the USERS_FACEBOOK table and use users_id
     * of use that we find by email address
     *
     * and then also insert avatar_external into USERS
     *
     * @todo create username for user based on Facebook username
     * Facebook does not really have username, so we can use fn_ln
     *
     */
    protected function createNewUser() {
        d('cp');
        $this->oRegistry->Mongo->USERS->ensureIndex(array('fb_id' => 1));

        /**
         * Time zone offset in seconds
         * @var int
         */
        $tzo = (array_key_exists('timezone', $this->aFbUserData)) ? $this->aFbUserData['timezone'] * 3600 : Cookie::get('tzo', 0);

        /**
         * User language
         * @var string
         */
        $lang = (!empty($this->aFbUserData['locale'])) ? \strtolower(\substr($this->aFbUserData['locale'], 0, 2)) : $this->oRegistry->getCurrentLang();

        /**
         * User locale
         * @var string
         */
        $locale = (!empty($this->aFbUserData['locale'])) ? $this->aFbUserData['locale'] : $this->oRegistry->Locale->getLocale();

        $this->tempPassword = String::makePasswd();

        /**
         * Sid value use existing cookie val
         * if possible, otherwise create a new one
         * @var string
         */
        $sid = (false === ($sid = Cookie::getSidCookie())) ? String::makeSid() : $sid;

        $displayName = (!empty($this->aFbUserData['name'])) ? $this->aFbUserData['name'] : $this->aFbUserData['first_name'] . ' ' . $this->aFbUserData['last_name'];
        $username = $this->makeUsername($displayName);

        /**
         * Create new record in USERS table
         * do this first because we need uid from
         * newly created record
         */
        $aUser = array(
            'username' => $username,
            'username_lc' => \mb_strtolower($username, 'utf-8'),
            'fn' => $this->aFbUserData['first_name'],
            'ln' => $this->aFbUserData['last_name'],
            'dn' => $displayName,
            'rs' => $sid,
            'email' => Utf8String::factory($this->aFbUserData['email'])->toLowerCase()->valueOf(),
            'fb_id' => (string) $this->aFbUserData['id'],
            'fb_token' => $this->aCookieParams['access_token'],
            'pwd' => String::hashPassword($this->tempPassword),
            'avatar_external' => 'http://graph.facebook.com/' . $this->aFbUserData['id'] . '/picture',
            'i_reg_ts' => time(),
            'date_reg' => date('r'),
            'role' => 'external_auth',
            'lang' => $lang,
            'i_rep' => 1,
            'tz' => TimeZone::getTZbyoffset($tzo),
            'i_fv' => (false !== $intFv = Cookie::getSidCookie(true)) ? $intFv : time());

        if (!empty($this->aFbUserData['gender'])) {
            $aUser['gender'] = ('male' === $this->aFbUserData['gender']) ? 'M' : 'F';
        }

        $aUser = \array_merge($this->oRegistry->Geo->Location->data, $aUser);

        if (!empty($this->aFbUserData['locale'])) {
            $aUser['locale'] = $this->aFbUserData['locale'];
        }

        if (!empty($this->aFbUserData['link'])) {
            $aUser['fb_url'] = $this->aFbUserData['link'];
        }

        d('aUser: ' . print_r($aUser, 1));

        $this->oUser = UserFacebook::factory($this->oRegistry, $aUser);
        $this->oUser->insert();
        //$this->oUser->setNewUser();


        d('$this->oUser after insert: ' . print_r($this->oUser->getArrayCopy(), 1));
        $this->oRegistry->Dispatcher->post($this->oUser, 'onNewUser');
        $this->oRegistry->Dispatcher->post($this->oUser, 'onNewFacebookUser');
        d('cp');

        $this->saveEmailAddress();
        d('cp');

        /**
         * Create new record in USERS_FACEBOOK
         */
        $this->updateFbUserRecord();

        PostRegistration::createReferrerRecord($this->oRegistry, $this->oUser);

        $this->postRegistrationToWall();

        return $this;
    }

    /**
     * Create new record in EMAILS table for this new user
     * but only if user has provided email address
     *
     * @return object $this
     */
    protected function saveEmailAddress() {
        if (!empty($this->aFbUserData['email'])) {
            $coll = $this->oRegistry->Mongo->EMAILS;
            $coll->ensureIndex(array('email' => 1), array('unique' => true));

            $a = array(
                'email' => \mb_strtolower($this->aFbUserData['email']),
                'i_uid' => $this->oUser->getUid(),
                'has_gravatar' => Gravatar::factory($this->aFbUserData['email'])->hasGravatar(),
                'ehash' => hash('md5', $this->aFbUserData['email']));
            try {
                $o = MongoDoc::factory($this->oRegistry, 'EMAILS', $a)->insert();
            } catch (\Exception $e) {
                e('Unable to save email address from Facebook to our EMAILS: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on ' . $e->getLine());
            }
        }

        return $this;
    }

    /**
     * Create a new record in USERS_FACEBOOK table
     * or update an existing record
     *
     * @param bool $isUpdate
     *
     * @return object $this
     */
    protected function updateFbUserRecord() {
        d('cp');

        $uid = $this->oUser->getUid();
        d('uid ' . $uid);

        $aFb = array(
            '_id' => (string) $this->aFbUserData['id'],
            'i_uid' => $uid,
            'access_token' => $this->aCookieParams['access_token'],
            'token_expiration' => (array_key_exists('expires', $this->aCookieParams)) ? $this->aCookieParams['expires'] : 0,
            'a_data' => $this->aFbUserData);

        d('aFb: ' . print_r($aFb, 1));

        $this->oRegistry->Mongo->USERS_FACEBOOK->save($aFb, array('fsync' => true));
        d('cp');

        return $this;
    }

    /**
     * @todo translate strings
     *
     * @todo have site logo image in settings
     *
     * @todo unfinished. Must ask user permission
     * during registration to post something to the wall
     * like "I joined this cool site"
     *
     * Post to user wall
     *
     */
    protected function postRegistrationToWall() {

        return $this;

        d('bToWall: ' . $this->bToWall);
        if ($this->bToWall) {
            $aData = array(
                'access_token' => $this->aCookieParams['access_token'],
                'message' => 'Joined this website',
                'link' => $this->oRegistry->Ini->SITE_URL,
                'caption' => 'Interesting stuff',
                'name' => $this->oRegistry->Ini->SITE_TITLE,
                'description' => '<b>Cool stuff </b>');
        }

        return $this;
    }

    /**
     * Add Facebook token and stuff to existing user
     *
     * Logic:
     * 1) If there is already another user with same
     * Facebook account - throw exception - must be unique
     *
     * 2) If this user is already connected to this same FB account -
     * this is OK, just update FB and User records
     *
     * 3) If Facebook's email address belongs to another user -?
     * It should not really be a problem in this case. This means
     * that someone (probably this same user) already has an account
     * on this site but it's a different account. So NOW this user is
     * connecting his second account to Facebook. This should not
     * cause any problems in the future.
     *
     *
     * @param User $oUser
     * @return object $this
     */
    public function connect(User $oUser) {
        d('cp');
        $this->oUser = $oUser;
        if (!empty($this->aCookieParams['uid'])) {
            d('cp');
            $this->checkUniqueAccount($this->getUserArray($this->aCookieParams['uid']));
        }

        $this->getFbData();
        d('cp');

        $this->checkUniqueAccount($this->getUserArray($this->aFbUserData['id']));
        d('cp');
        /**
         * Now we need to check again if
         * another user already uses this Facebook account
         *
         * If NOT then create new Facebook record
         * and add Facebook credentials to existing user
         * NOT going to create new user!
         */
        $this->updateUser(false)->updateFbUserRecord();
        d('cp');

        return $this;
    }

    /**
     *
     * Get array of data for user by the value
     * of fb_id in USERS collection
     *
     * @param mixed $fb_id
     * @return mixed null|array
     *
     */
    protected function getUserArray($fb_id) {
        $fb_id = (string) $fb_id;

        return $this->oRegistry->Mongo->USERS->findOne(array('fb_id' => $fb_id));
    }

    /**
     *
     * Validation to check that user represented by
     * $aUser array is the same account as $this->oUser
     *
     * @param array $aUser
     * @throws \StudyBuddy\Exception is user from input array
     * is different from $this->oUser.
     *
     * @return object $this
     *
     */
    protected function checkUniqueAccount(array $aUser = null) {
        if (!is_object($this->oUser) || (!$this->oUser instanceof \StudyBuddy\User)) {
            d('$this->oUser now set yet');

            throw new DevException('$this->oUser now set yet');
        }

        if (!empty($aUser) && ((int) $aUser['_id'] !== $this->oUser->getUid())) {
            d('Different user already exists');

            throw new Exception('This Facebook account is already connected to another user <strong>' . $aUser['fn'] . ' ' . $aUser['ln'] . '</strong><br>
				<br>A Facebook account cannot be associated with more than one account on this site<br>');
        }

        d('cp');

        return $this;
    }

    /**
     * @todo sent a welcome email,
     * include temp password and explain to user
     * that user can keep logging in with facebook connect
     * OR using email address and password
     *
     * Enter description here ...
     */
    protected function sendWelcomeEmail() {
        
    }

}
